package com.lry.basic.spring.ioc;
import com.lry.basic.spring.ioc.util.Common;
import com.sun.org.apache.xpath.internal.operations.Bool;
import org.junit.platform.commons.util.StringUtils;

import java.io.Closeable;
import java.io.IOException;
import java.lang.reflect.*;
import java.util.*;
import java.util.concurrent.ConcurrentHashMap;
/**
 * @author:刘仁有
 * @desc:
 * @email:953506233@qq.com
 * @data:2019/11/5
 */
public class DefaultBeanFactory implements BeanFactory,BeanDefinitionRegistry, Closeable {
    //bd集合
    private Map<String,BeanDefinition> beanDefinitions = new ConcurrentHashMap<>(256);
    //单例池
    private Map<String,Object> singletonObjects = new ConcurrentHashMap<>(256);
    //早期单例池
    private Map<String,Object> earlySingletonObjects = new ConcurrentHashMap<>(256);
    //正在创建的spring beanName的集合
    private ThreadLocal<Set<String>> buildingBeans = new ThreadLocal<>();
    //bd name 集合
    private List<String> beanDefinitionNames = new ArrayList<>();
    //BeanPostProcessor 集合
    private List<BeanPostProcessor> beanPostProcessors = Collections.synchronizedList(new ArrayList<>());
    ////BeanFactoryPostProcessor 集合
    private List<BeanFactoryPostProcessor> beanFactoryPostProcessors = Collections.synchronizedList(new ArrayList<>());
    @Override
    public void registerBeanDefinition(String beanName, BeanDefinition bd) {
        Objects.requireNonNull(beanName,"spring bean name must not null at registerBeanDefinition");
        Objects.requireNonNull(bd,"spring bd must not null");
        if(!bd.validate()){
            throw new RuntimeException("BeanDefinition named with"+beanName+"is invalid");
        }
        if(beanDefinitions.containsKey(beanName)){
            throw new RuntimeException("beanDefinitions must not contain same beanName:"+beanName);
        }
        beanDefinitions.put(beanName,bd);
        beanDefinitionNames.add(beanName);
    }

    @Override
    public BeanDefinition getBeanDefinition(String beanName) {
        return beanDefinitions.get(beanName);
    }

    @Override
    public void removeBeanDefinition(String beanName) {
        beanDefinitions.remove(beanName);
    }

    @Override
    public boolean containsBeanDefinition(String beanName) {
        return beanDefinitions.containsKey(beanName);
    }

    @Override
    public Object getBean(String beanName) throws Exception {
        Objects.requireNonNull(beanName,"spring bean name must not null at getBean");
        return this.doGetBean(beanName);
    }

    @Override
    public void registerBeanPostProcessor(BeanPostProcessor beanPostProcessor) {
        if(beanPostProcessor instanceof BeanFactoryAware){
            ((BeanFactoryAware)beanPostProcessor).setBeanFactory(this);
        }
        this.beanPostProcessors.add(beanPostProcessor);
    }

    @Override
    public void registerBeanFactoryPostProcessor(BeanFactoryPostProcessor beanFactoryPostProcessor) {
        this.beanFactoryPostProcessors.add(beanFactoryPostProcessor);
    }


    public List<BeanFactoryPostProcessor> getBeanFactoryPostProcessors() {
        return beanFactoryPostProcessors;
    }

    public Map<String, BeanDefinition> getBeanDefinitions() {
        return beanDefinitions;
    }

    /**
     * 单例池取
     * 取不出，bd new一个
     * 决策构造器，newInstance()
     * 空对象==暂无属性
     * 加入早期单例池
     * 属性注入
     * 循环依赖问题发现（定义一个正在创建的bean集合）
     * 循环依赖问题（二级缓存解决）
     * 调用init方法
     * 加入单例池，移除早期单例池
     * ioc完毕
     * spring 扩展点BeanPostProcessors
     * @param beanName
     * @return
     */
    protected Object doGetBean(String beanName) throws Exception {
        BeanDefinition bd = beanDefinitions.get(beanName);
        if(bd==null){
            return null;
        }
        Object instance = null;
        //单例直接从单例池拿
        if(bd.isSingleton()){
            instance = this.getSingleton(beanName);
            if(instance!=null){
                return instance;
            }
        }
        //初始化正在创建的bean集合
        Set<String>ingBeans = buildingBeans.get();
        if(ingBeans==null){
            ingBeans = new HashSet<>();
            this.buildingBeans.set(ingBeans);
        }
        //循环依赖检测
        //构造器之间的循环依赖无法解决
        //属性之间的循环依赖可以解决
        //属性+构造器 可以解决
        //构造器+属性不可以解决
        if(ingBeans.contains(beanName)){
            throw new Exception(beanName + " 循环依赖！" + ingBeans);
        }
        ingBeans.add(beanName);

        //决策构造器创建对象
        instance = createInstance(bd);

        //放到早期单例池 解决部分循环依赖
        if(bd.isSingleton()){
            this.earlySingletonObjects.put(beanName,instance);
        }

        //属性注入DI
        setPropertyValues(bd,instance);
        instance = this.applyPostProcessBeforeInitialization(instance, beanName);
        //回调初始化方法
        doInit(bd,instance);
        instance = this.applyPostProcessAfterInitialization(instance, beanName);
        //单例放入单例池
        if(bd.isSingleton()){
            singletonObjects.put(beanName,instance);
            earlySingletonObjects.remove(beanName);
        }
        ingBeans.remove(beanName);
        return instance;
    }

    // 应用bean初始化前的处理
    private Object applyPostProcessBeforeInitialization(Object bean, String beanName) {
        for (BeanPostProcessor bpp : this.beanPostProcessors) {
            bean = bpp.postProcessBeforeInitialization(bean, beanName);
        }
        return bean;
    }

    // 应用bean初始化后的处理
    private Object applyPostProcessAfterInitialization(Object bean, String beanName){
        for (BeanPostProcessor bpp : this.beanPostProcessors) {
            bean = bpp.postProcessAfterInitialization(bean, beanName);
        }
        return bean;
    }



    private Object getSingleton(String beanName){
        //单例池中拿 第一级缓存
        Object singletonObject = this.singletonObjects.get(beanName);
        //单例池没有
        if (singletonObject == null){
            //从早期单例池拿
            synchronized (this.singletonObjects){
                //early中拿 第二级缓存
                singletonObject = this.earlySingletonObjects.get(beanName);
            }
        }
        return singletonObject;
    }

    private void setPropertyValues(BeanDefinition bd, Object instance) throws Exception {
        if(null==instance||null==bd.getPropertyValues()||bd.getPropertyValues().isEmpty())return;
        for(PropertyValue pv:bd.getPropertyValues()){
            if(StringUtils.isBlank(pv.getName())){
                continue;
            }
            Class clazz = instance.getClass();
            Field field = clazz.getDeclaredField(pv.getName());
            field.setAccessible(true);
            Object realVal = Common.getBaseValue(field.getType(),getRealValue(pv.getValue()));
            field.set(instance,realVal);
        }
    }


    public Object getRealValue(Object value) throws Exception {
        Object realValue = null;
        if(value==null){
            realValue = null;
        }else if (value instanceof BeanReference) {
            realValue = this.doGetBean(((BeanReference) value).getBeanName());
        } else if (value instanceof Object[]) {
            Object[] objs = (Object[]) value;
            for (int i=0;i<objs.length;i++) {
                objs[i] = getRealValue(objs[i]);
            }
            realValue = objs;
        } else if (value instanceof Collection) {
            Collection collection = (Collection) value;
            Object[] objs = collection.toArray();
            for (int i=0;i<objs.length;i++) {
                objs[i] = getRealValue(objs[i]);
            }
            //todo
            if(value instanceof List){
                List list= Arrays.asList(objs);
                if(value instanceof ArrayList){
                    realValue = Arrays.asList(objs);
                }else if(value instanceof LinkedList){
                    LinkedList ll = new LinkedList();
                    ll.addAll(list);
                    realValue = ll;
                }else if(value instanceof Vector){
                    Vector v = new Vector();
                    v.addAll(list);
                    realValue = v;
                }
            }else if(value instanceof Set){
                if(value instanceof HashSet){

                }else if(value instanceof LinkedHashSet){

                }else if(value instanceof SortedSet){

                }else if(value instanceof TreeSet){

                }
            }

        } else if (value instanceof Properties) {
            // TODO 处理properties中的bean引用
        } else if (value instanceof Map) {
            // TODO 处理Map中的bean引用
        } else{
            realValue = value;
        }
        return realValue;
    }

    private Object createInstance(BeanDefinition bd) throws Exception {
        if(bd.getBeanClass()!=null){
            //beanClass
            if(StringUtils.isBlank(bd.getFactoryMethodName())){
                return createInstanceByConstructor(bd);
            }else{  //beanClass(工厂的class)+factoryMethodName 静态工厂方法
                return createInstanceByStaticFactoryMethod(bd);
            }
        }else{ //factoryMethodName+factoryBeanName（用spring的工厂bean调用工厂里的普通工厂方法产生实例对象）
            return createInstanceByFactoryMethod(bd);
        }
    }

    private Object createInstanceByFactoryMethod(BeanDefinition bd) throws Exception {
        Object factoryBean = this.doGetBean(bd.getFactoryBeanName());
        Method method = factoryBean.getClass().getMethod(bd.getFactoryMethodName(),null);
        return method.invoke(factoryBean,null);
    }

    private Object createInstanceByStaticFactoryMethod(BeanDefinition bd) throws Exception {
        Method method = bd.getBeanClass().getMethod(bd.getFactoryMethodName(),null);
        return method.invoke(bd.getBeanClass(),null);
    }

    private Object createInstanceByConstructor(BeanDefinition bd)throws Exception {
        Object[] constructorArgs = this.getConstructorArgumentValues(bd);//构造器参数
        if(null==constructorArgs||constructorArgs.length==0){
            return bd.getBeanClass().newInstance();
        }
        bd.setConstructorArgumentRealValues(constructorArgs);
        return this.determineConstructor(bd,constructorArgs).newInstance(constructorArgs);
    }

    private Object[] getConstructorArgumentValues(BeanDefinition bd) throws Exception {
        return this.getRealValues(bd.getConstructorArgumentValues());
    }

    private Object[] getRealValues(List<?> constructorArgumentValues) throws Exception {
        if(null==constructorArgumentValues||constructorArgumentValues.isEmpty())return null;
        Object[] values = new Object[constructorArgumentValues.size()];
        int i = 0;
        Object realValue = null;
        for (Object value:constructorArgumentValues) {
            if(value==null){
                realValue = null;
            }else if (value instanceof BeanReference) {
                realValue = this.doGetBean(((BeanReference) value).getBeanName());
            } else if (value instanceof Object[]) {
                // TODO 处理集合中的bean引用
            } else if (value instanceof Collection) {
                // TODO 处理集合中的bean引用
            } else if (value instanceof Properties) {
                // TODO 处理properties中的bean引用
            } else if (value instanceof Map) {
                // TODO 处理Map中的bean引用
            } else{
                realValue = value;
            }
            values[i++] = realValue;
        }
        return values;
    }

    private Constructor determineConstructor(BeanDefinition bd,Object[] constructorArgs) throws Exception {
        Constructor<?>[] constructors = bd.getBeanClass().getConstructors();
        Constructor con = null;
        out:for (Constructor constructor :constructors) {
            Class[] paramTypes = this.getParameterTypes(constructor.getParameterTypes());
            //构造器参数长度匹配
            if(null!=paramTypes&&paramTypes.length==constructorArgs.length){
                //构造器参数类型完全匹配
                for(int i=0;i<paramTypes.length;i++){
                    if(!paramTypes[i].isAssignableFrom(constructorArgs[i].getClass())){
                        continue out;
                    }
                }
                con = constructor;
                break out;
            }
        }
        if(con==null){
            throw new RuntimeException(bd.getBeanClass().getName()+":no found right constructor");
        }
        return con;
    }

    private Class[] getParameterTypes(Class[] paramTypes) {
        for(int i=0;i<paramTypes.length;i++){
            switch (paramTypes[i].getName()){
                //short byte long int double float char boolean
                case "short":
                    paramTypes[i] = Short.class;
                    break;
                case "byte":
                    paramTypes[i] = Byte.class;
                    break;
                case "long":
                    paramTypes[i] = Long.class;
                    break;
                case "int":
                    paramTypes[i] = Integer.class;
                    break;
                case "double":
                    paramTypes[i] = Double.class;
                    break;
                case "float":
                    paramTypes[i] = Float.class;
                    break;
                case "char":
                    paramTypes[i] = Character.class;
                    break;
                case "boolean":
                    paramTypes[i] = Boolean.class;
                    break;
            }
        }
        return paramTypes;
    }


    public void doInit(BeanDefinition bd,Object instance) throws Exception {
        if(StringUtils.isNotBlank(bd.getInitMethodName())){
            Method method = instance.getClass().getMethod(bd.getInitMethodName(),null);
            method.invoke(instance,null);
        }
    }
    @Override
    public void close() throws IOException {
        for (Map.Entry<String, BeanDefinition> entry : this.beanDefinitions.entrySet()){
            String beanName = entry.getKey();
            BeanDefinition beanDefinition = entry.getValue();
            if (beanDefinition.isSingleton() && StringUtils.isNotBlank(beanDefinition.getDestroyMethodName())){
                Object instance = this.singletonObjects.get(beanName);
                try {
                    Method method = instance.getClass().getMethod(beanDefinition.getDestroyMethodName(),null);
                    method.invoke(instance,null);
                }catch (NoSuchMethodException|SecurityException|IllegalAccessException|IllegalArgumentException|InvocationTargetException e){
                    System.err.println("执行bean["+beanName+"] "+beanDefinition+"的销毁方法异常");
                }
            }
        }
    }

}
